﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace AMDev.BNET.ClientAPI.WoW
{
    /*
     * The code uses BNETApiMethod implementation to know the REST API querystring format. 
     * Maybe some methods must be optimized
     */

    /// <summary>
    /// Allow access to World of Warcraft REST API
    /// </summary>
    public class WoWAPIClient
        : BNETBaseClient
    {

        #region .ctor

        public WoWAPIClient()
            : base()
        {
        }

        public WoWAPIClient(RegionsEnumeration region)
            : base(region)
        {
        }
        
        #endregion

        #region Methods

        #region Async read methods

        /// <summary>
        /// Get the statuses of all realms for the selected Battle.Net zone.
        /// </summary>
        /// <returns>A BNETResponse object filled with an array of WoWRealmsStatus objects</returns>
        /// <remarks>
        /// The method is asyncronous and uses the new await and async keywords for Visual Studio 2012. 
        /// </remarks>
        /// <see cref="WoWRealmsStatus"/>
        /// <see cref="BNETResponse"/>
        /// <seealso cref="http://us.battle.net/wow/en/forum/topic/2369741469"/>
        [BNETApiMethod(Function = "realm/status", Context = "api/wow")]
        public async Task<BNETResponse> GetRealmsStatusesAsync()
        {
            String functionUri = null;
            String serviceUri = null;
            String restData = null;
            WoWRealmStatusResponse restResponse = null;
            WoWRealm[] realmsStatuses = null;
            JSONResponseDeserializer<WoWRealmStatusResponse> responseDeserializer = null;
            BNETResponse response = null;

            functionUri = this.GetRestAPIFunctionFromMethod();
            serviceUri = this.BuildServiceUri(functionUri);

            restData = await this.GetRestDataAsync(serviceUri);

            if (!String.IsNullOrWhiteSpace(restData))
            {
                try
                {   
                    responseDeserializer = new JSONResponseDeserializer<WoWRealmStatusResponse>();
                    restResponse = responseDeserializer.Deserialize(restData);
                    realmsStatuses = restResponse.Realms.ToArray();
                    response = new BNETResponse(realmsStatuses);
                }
                catch (SerializationException serExc)
                {                   
                    response = new BNETResponse(null, true, BNETErrorValues.SerializationError, serExc.ToString());                    
                }
            }

            return response;
        }

        /// <summary>
        /// Get the statuses of the selected realms for the selected Battle.Net zone.
        /// </summary>
        /// <param name="realms">An array of strings containing the desired realms to check for</param>
        /// <returns>A BNETResponse object filled with an array of WoWRealmsStatus objects</returns>
        /// <remarks>
        /// The method is asyncronous and uses the new await and async keywords for Visual Studio 2012. 
        /// </remarks>
        /// <see cref="WoWRealmsStatus"/>
        /// <see cref="BNETResponse"/>
        /// <seealso cref="http://us.battle.net/wow/en/forum/topic/2369741469"/>
        [BNETApiMethod(Function = "realm/status", Context = "api/wow", ParametersFormat = "?realms={0}")]
        public async Task<BNETResponse> GetSelectedRealmsStatusesAsync(String[] realms)
        {
            String functionUri = null;
            String serviceUri = null;
            String restData = null;
            StringBuilder realmsList = null;
            WoWRealmStatusResponse restResponse = null;
            WoWRealm[] realmsStatuses = null;
            JSONResponseDeserializer<WoWRealmStatusResponse> responseDeserializer = null;
            BNETResponse response = null;

            if (realms == null)
                throw new ArgumentNullException("realms");

            functionUri = this.GetRestAPIFunctionFromMethod();
            serviceUri = this.BuildServiceUri(functionUri);

            realmsList = new StringBuilder();

            for (int i = 0; i < realms.Length; i++)
            {
                realmsList.Append(realms[0]);
                if (i + 1 < realmsList.Length)
                    realmsList.Append(",");
            }

            serviceUri = String.Format(serviceUri, realmsList.ToString());

            try
            {
                restData = await this.GetRestDataAsync(serviceUri);

                if (!String.IsNullOrWhiteSpace(restData))
                {
                    responseDeserializer = new JSONResponseDeserializer<WoWRealmStatusResponse>();
                    restResponse = responseDeserializer.Deserialize(restData);

                    // TODO: Check for 404 or 500 error statuses

                    realmsStatuses = restResponse.Realms.ToArray();
                    response = new BNETResponse(realmsStatuses);
                }
            }
            catch (FormatException fExc)
            {
                response = new BNETResponse(null, BNETErrorValues.FormatError, fExc.ToString());
            }
            catch (SerializationException serExc)
            {
                response = new BNETResponse(null, true, BNETErrorValues.SerializationError, serExc.ToString());
            }
            catch (Exception exc)
            {
                response = new BNETResponse(null, true, BNETErrorValues.Unknown, exc.ToString());
            }

            return response;
        }

        [BNETApiMethod(Function = "realm/status", Context = "api/wow", ParametersFormat="?realms={0}")]
        public async Task<BNETResponse> GetRealmStatusAsync(string realmName)
        {
            WoWRealm[] realmsStatuses = null;
            BNETResponse response = null;
            WoWRealm selectedRealmStatus = null;

            if (String.IsNullOrEmpty(realmName))
                throw new ArgumentNullException("realmName");

            response = await this.GetSelectedRealmsStatusesAsync(new String[] { realmName });

            if (!response.HasError)
            {
                realmsStatuses = response.ResponseData as WoWRealm[];
                if (realmsStatuses != null)
                {
                    if (realmsStatuses.Length > 1)
                        selectedRealmStatus = realmsStatuses.Where(el => el.Name == realmName).FirstOrDefault();
                    else
                        selectedRealmStatus = realmsStatuses[0];
                    response = new BNETResponse(selectedRealmStatus);
                }
                else
                    response = new BNETResponse(null, BNETErrorValues.GenericError, "Cannot convert response data");
            }
            return response;
        }

        [BNETApiMethod(Function="character", Context="api/wow", ParametersFormat="/{0}/{1}")]
        public async Task<BNETResponse> GetCharacterProfileAsync(String realm, String characterName, WoWCharacterProfileCaps caps)
        {
            String functionUri = null;
            String restData = null;
            StringBuilder serviceUri = null;
            JSONResponseDeserializer<WoWCharacter> responseDeserializer = null;
            WoWCharacter character = null;
            BNETResponse response = null;
            bool appendFieldsParameter = false;

            serviceUri = new StringBuilder();
            functionUri = this.GetRestAPIFunctionFromMethod();

            serviceUri.AppendFormat(this.BuildServiceUri(functionUri), realm, characterName);

            if (caps != WoWCharacterProfileCaps.Default)
            {
                serviceUri.Append("?");
                serviceUri.Append("fields=");

                if ((caps & WoWCharacterProfileCaps.Stats) == WoWCharacterProfileCaps.Stats)
                {
                    if (appendFieldsParameter == false)
                        appendFieldsParameter = true;
                    else
                        serviceUri.Append(",");
                    serviceUri.Append("stats");
                }

                if ((caps & WoWCharacterProfileCaps.Talents) == WoWCharacterProfileCaps.Talents)
                {
                    if (appendFieldsParameter == false)
                        appendFieldsParameter = true;
                    else
                        serviceUri.Append(",");
                    serviceUri.Append("talents");
                }

                if ((caps & WoWCharacterProfileCaps.Items) == WoWCharacterProfileCaps.Items)
                {
                    if (appendFieldsParameter == false)
                        appendFieldsParameter = true;
                    else
                        serviceUri.Append(",");
                    serviceUri.Append("items");
                }
            }

            try
            {
                restData = await this.GetRestDataAsync(serviceUri.ToString());

                if (!String.IsNullOrWhiteSpace(restData))
                {
                    responseDeserializer = new JSONResponseDeserializer<WoWCharacter>();
                    try
                    {
                        character = responseDeserializer.Deserialize(restData);
                        response = new BNETResponse(character);
                    }
                    catch (SerializationException serExc)
                    {
                        response = new BNETResponse(null, BNETErrorValues.SerializationError, serExc.ToString());
                    }
                    catch (FormatException fExc)
                    {
                        response = new BNETResponse(null, BNETErrorValues.FormatError, fExc.ToString());
                    }
                }
            }
            catch (Exception exc)
            {
                response = new BNETResponse(null, BNETErrorValues.Unknown, exc.ToString());
            }

            return response;
        }

        #endregion

        public String GenerateStaticRenderUri(String filename)
        {
            StringBuilder uriBuilder = null;

            uriBuilder = new StringBuilder();
            
            if (!String.IsNullOrWhiteSpace(filename))
            {   
                uriBuilder.Append("http://");
                uriBuilder.Append(this.Region.GetHost());
                uriBuilder.Append("/static-render/");
                uriBuilder.Append(this.Region.GetRegion());
                uriBuilder.Append("/");
                uriBuilder.Append(filename);
            }

            return uriBuilder.ToString();
        }

        #endregion
    }
}
